﻿using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using FrameWork.GamePool;

namespace FrameWork.Managers
{
    public class PoolManager : BaseManager
    {
        private static class TypeStore<T> where T : class, IRecycleAble
        {
            public static Pool<T> pool;
        }

        internal PoolManager(ConstructorToken constructorToken = null) : base(constructorToken)
        {
        }

        private static void RegisterFactory<T>(Func<T> factory) where T : class, IRecycleAble
        {
            TypeStore<T>.pool = new Pool<T>(factory);
        }
        
        private sealed class Pool<T> where T : IRecycleAble
        {
            private readonly Stack<T> _stack = new Stack<T>();
            private readonly Func<T> _createFn;
            
            public Pool(Func<T> createFn) => _createFn = createFn;

            public T Get()
            {
                var item = _stack.Count == 0 ? _createFn() : _stack.Pop();
                item.OnGet();
                return item;
            }

            public void Recycle(T element)
            {
                element.OnRecycle();
                _stack.Push(element);
            }
        }
        
        
        public T Get<T>() where T : class, IRecycleAble
        {
            if (TypeStore<T>.pool == null)
            {
                throw new InvalidOperationException(
                    $"未为类型 {typeof(T).Name} 注册工厂方法。请先调用 RegisterFactory<{typeof(T).Name}>()");
            }
        
            return TypeStore<T>.pool.Get();
        }
        
        public void Recycle<T>(T element) where T : class, IRecycleAble
        {
            if (element == null) return;
        
            if (TypeStore<T>.pool == null)
            {
                throw new InvalidOperationException(
                    $"未为类型 {typeof(T).Name} 注册工厂方法");
            }
        
            TypeStore<T>.pool.Recycle(element);
        }

        public override UniTask OnInit()
        {
            RegisterFactory(TestNode.CreateSelf);
            return UniTask.CompletedTask;
        }
    }
}